package com.liuyi.security.vote;

import lombok.AllArgsConstructor;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.FilterInvocation;

import java.util.Collection;

/**
 * @author Mr.fmy
 * @version 1.0.0
 * @ClassName JdbcVote
 * @Description TODO 可连接jdbc的权限验证投票类
 * <p>
 * 该投票类 {@link UnanimousBased}认证器使用，用 {@link org.springframework.security.web.access.expression.WebExpressionVoter} 在前
 * 该类在后比较合适，具体可查看
 * 使用该方式，最好配置缓存，且不要单单存在redis缓存，如果只存在redis缓存速度也会慢，毕竟redis也有带宽
 * </p>
 * @createTime 2020/4/29 16:29
 */
@AllArgsConstructor
public class JdbcVote implements AccessDecisionVoter<FilterInvocation> {

    private final UriAuthority uriAuthority;

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

    @Override
    public int vote(Authentication authentication, FilterInvocation filterInvocation, Collection<ConfigAttribute> collection) {
        String requestURI = filterInvocation.getRequest().getRequestURI();
        Collection<? extends GrantedAuthority> uriAuthority = getUriAuthority(requestURI);
        //如果当前uri未有权限、则表示弃权
        if (uriAuthority == null || uriAuthority.size() == 0) {
            return ACCESS_ABSTAIN;
        }
        //如果当前 authentication 为空，则表示拒绝
        if (authentication == null || authentication.getAuthorities().size() == 0) {
            return ACCESS_DENIED;
        }
        //鉴权
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for (GrantedAuthority authority : authorities) {
            if (authorities.contains(authority)) {
                return ACCESS_GRANTED;
            }
        }
        //如果鉴权失败、则表示拒绝
        return ACCESS_DENIED;
    }

    /**
     * 获取权限
     */
    private Collection<? extends SimpleGrantedAuthority> getUriAuthority(String uri) {
        return uriAuthority.getUriAuthority(uri);
    }

    public interface UriAuthority {

        /**
         * 缓存前戳，如果没有缓存，就不要使用该方式了
         */
        String SECURITY_AUTHORI_URI = "security_authori_uri_";

        /**
         * 根据uri获取该uri访问需要的权限
         *
         * @param uri uri
         * @return 权限集合
         */
        Collection<? extends SimpleGrantedAuthority> getUriAuthority(String uri);
    }
}
